/*
 * Copyright 2024 The CHANGLEI Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import 'dart:convert';
import 'dart:io';

import 'package:fibber/src/logger.dart';
import 'package:fibber/src/spec.dart';
import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
import 'package:yaml_edit/yaml_edit.dart';

const _flutterKey = r'flutter';
const _assetsKey = r'assets';

Future<void> writePubspecYaml(File pubspecFile, List<String> directories) async {
  final content = pubspecFile.readAsStringSync();

  final yamlMap = loadYaml(content) as Map?;
  final flutterNode = yamlMap?[_flutterKey] as Map?;
  if (yamlMap == null || flutterNode == null) {
    Level.error.writeln('不是一个`flutter`项目，或者添加`flutter`节点。');
    return;
  }

  final yamlContents = <String>[
    r'flutter:',
    '${indent}assets:',
    ...directories.map((e) => '${indent * 2}- $e'),
  ];

  final assetsNode = flutterNode[_assetsKey] as List?;
  final assetsYamlList = List.of(_resolveAssets(assetsNode, directories));
  try {
    final yamlEditor = YamlEditor(content);
    yamlEditor.update([_flutterKey, _assetsKey], assetsYamlList);
    final newContent = yamlEditor.toString();
    if (newContent != content) {
      final writer = pubspecFile.openWrite();
      await writer.flush();
      writer.write(newContent);
      await writer.close();

      Level.verbose.writeln(r'Running -> flutter pub get');
      final process = await Process.start(r'flutter', [r'pub', r'get'], runInShell: true);
      final subscription = process.stdout.transform(utf8.decoder).listen(Level.verbose.writeln);
      await process.exitCode;
      await subscription.cancel();
    }

    Level.warn.writeAll(
      <String>[
        r'已在`pubspec.yaml`中添加以下内容：',
        ...yamlContents,
      ],
      '\n',
    );
  } catch (e) {
    Level.warn.writeAll(
      <String>[
        r'请在`pubspec.yaml`中添加以下内容：',
        ...yamlContents,
      ],
      '\n',
    );
    rethrow;
  } finally {
    Level.normal.writeln();
  }
}

Iterable<String> _resolveAssets(List<dynamic>? assetsNode, List<String> directories) sync* {
  final newAssets = List.of(directories);
  if (assetsNode == null) {
    yield* newAssets;
    return;
  }
  for (final node in assetsNode) {
    yield node as String;
    newAssets.removeWhere((element) => path.equals(node, element) || path.isWithin(node, element));
  }
  yield* newAssets;
}
